#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kfifo.h>

//多个cdev管理多个设备
#define CHAR_DEV_MAJOR 256
#define CHAR_DEV_MINOR 0
#define CHAR_DEV_CNT 2
#define CHAR_DEV_NAME "one-driver-more-device-1"

//定义多个fifo
DEFINE_KFIFO(vsfifo0, char, 32);
DEFINE_KFIFO(vsfifo1, char, 32);

//封装cdev和fifo
struct vser_dev
{
    struct kfifo *fifo;
    struct cdev cdev;
};

//定义vser_dev的对象
static struct vser_dev vsdev[CHAR_DEV_CNT];

static int one_driver_more_dev_2_open(struct inode *inode, struct file *filp)
{
    //给私人数据赋值
    filp->private_data = container_of(inode->i_cdev, struct vser_dev, cdev);

    return 0;
}

static int one_driver_more_dev_2_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t one_driver_more_dev_2_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
    int ret;
    unsigned int copied = 0;
    struct vser_dev *dev = filp->private_data;

    ret = kfifo_to_user(dev->fifo, buf, count, &copied);

    return ret == 0 ? copied : ret;
}

static ssize_t one_driver_more_dev_2_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
    int ret;
    unsigned int copied = 0;
    struct vser_dev *dev = filp->private_data;

    ret = kfifo_from_user(dev->fifo, buf, count, &copied);

    return ret == 0 ? copied : ret;
}

// 用来定义操作字符设备的方法
static struct file_operations vsdev_ops = {
    .owner = THIS_MODULE,                     //只想当前模块
    .open = one_driver_more_dev_2_open,       //打开
    .release = one_driver_more_dev_2_release, //释放
    .read = one_driver_more_dev_2_read,       //读数据
    .write = one_driver_more_dev_2_write,     //写数据
};

static int __init one_driver_more_dev_2_driver_init(void)
{
    int i;
    int ret;
    dev_t dev;

    //合成设备号
    dev = MKDEV(CHAR_DEV_MAJOR, CHAR_DEV_MINOR);

    //注册设备号
    ret = register_chrdev_region(dev, CHAR_DEV_CNT, CHAR_DEV_NAME);
    if (ret)
        goto reg_err;

    //初始化字符设备
    for (i = 0; i < CHAR_DEV_CNT; i++)
    {
        cdev_init(&vsdev[i].cdev, &vsdev_ops);
        vsdev[i].cdev.owner = THIS_MODULE;
        vsdev[i].fifo = i == 0 ? (struct kfifo *)&vsfifo0 : (struct kfifo *)&vsfifo1;

        //将主设备号对255取余添加到cdev_map中,第三个参数取1,因为一个cdev结构体对应一个设备
        ret = cdev_add(&vsdev[i].cdev, dev + i, 1);
        if (ret)
            goto add_err;
    }

    return 0;
add_err:
    for (--i; i >= 0; --i)
        cdev_del(&vsdev[i].cdev);
    unregister_chrdev_region(dev, CHAR_DEV_CNT);
reg_err:
    return ret;
}

static void __exit one_driver_more_dev_2_driver_exit(void)
{
    int i;
    dev_t dev;

    //合成设备号
    dev = MKDEV(CHAR_DEV_MAJOR, CHAR_DEV_MINOR);

    //删除cdev
    for (i = 0; i < CHAR_DEV_CNT; i++)
        cdev_del(&vsdev[i].cdev);

    //注销设备号
    unregister_chrdev_region(dev, CHAR_DEV_CNT);
}

module_init(one_driver_more_dev_2_driver_init);
module_exit(one_driver_more_dev_2_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Biven Tang <bivent@163.com>");
MODULE_DESCRIPTION("one driver more device with more cdev!");
MODULE_ALIAS("one-driver-more-device-2");
